﻿using System;
using System.Collections;
using System.Data;
using System.Data.SqlClient;

namespace CodeFarmer.Core.Data
{
    /// <summary>
    /// 提供静态缓存用于存储参数，以使存储过程可以在运行时发现参数。
    /// </summary>
    public sealed class SqlHelperParameterCache
    {
        private static Hashtable paramCache = Hashtable.Synchronized(new Hashtable());
        private SqlHelperParameterCache()
        {
        }
        /// <summary>
        /// 在运行时发现存储过程的参数集合。
        /// </summary>
        /// <param name="connection">有效的数据库连接对象</param>
        /// <param name="spName">存储过程名称</param>
        /// <param name="includeReturnValueParameter">是否要包括返回值参数</param>
        /// <returns>待发现的参数数组</returns>
        private static SqlParameter[] DiscoverSpParameterSet(SqlConnection connection, string spName, bool includeReturnValueParameter)
        {
            if (connection == null)
            {
                throw new ArgumentNullException("connection");
            }
            if (spName == null || spName.Length == 0)
            {
                throw new ArgumentNullException("spName");
            }
            SqlCommand cmd = new SqlCommand(spName, connection);
            cmd.CommandType = CommandType.StoredProcedure;
            connection.Open();
            SqlCommandBuilder.DeriveParameters(cmd);
            connection.Close();
            if (!includeReturnValueParameter)
            {
                cmd.Parameters.RemoveAt(0);
            }
            int count = cmd.Parameters.Count;
            SqlParameter[] discoveredParameters = new SqlParameter[count];
            cmd.Parameters.CopyTo(discoveredParameters, 0);
            SqlParameter[] array = discoveredParameters;
            for (int i = 0; i < array.Length; i++)
            {
                SqlParameter discoveredParameter = array[i];
                discoveredParameter.Value = DBNull.Value;
            }
            return discoveredParameters;
        }
        /// <summary>
        /// 复制参数
        /// </summary>
        /// <param name="originalParameters"></param>
        /// <returns></returns>
        private static SqlParameter[] CloneParameters(SqlParameter[] originalParameters)
        {
            int length = originalParameters.Length;
            SqlParameter[] clonedParameters = new SqlParameter[length];
            int i = 0;
            int j = length;
            while (i < j)
            {
                clonedParameters[i] = (SqlParameter)((ICloneable)originalParameters[i]).Clone();
                i++;
            }
            return clonedParameters;
        }
        /// <summary>
        /// 增加参数数组到缓存
        /// </summary>
        /// <param name="connectionString">有效的数据库连接串</param>
        /// <param name="commandText">获取或设置要对数据源执行的 Transact-SQL 语句或存储过程</param>
        /// <param name="commandParameters">待缓存的参数数组</param>
        public static void CacheParameterSet(string connectionString, string commandText, params SqlParameter[] commandParameters)
        {
            if (connectionString == null || connectionString.Length == 0)
            {
                throw new ArgumentNullException("connectionString");
            }
            if (commandText == null || commandText.Length == 0)
            {
                throw new ArgumentNullException("commandText");
            }
            string hashKey = connectionString + ":" + commandText;
            SqlHelperParameterCache.paramCache[hashKey] = commandParameters;
        }
        /// <summary>
        /// 从缓存中得到参数数组
        /// </summary>
        /// <param name="connectionString">有效的数据库连接串</param>
        /// <param name="commandText">获取或设置要对数据源执行的 Transact-SQL 语句或存储过程</param>
        /// <returns>得到的参数数组</returns>
        public static SqlParameter[] GetCachedParameterSet(string connectionString, string commandText)
        {
            if (connectionString == null || connectionString.Length == 0)
            {
                throw new ArgumentNullException("connectionString");
            }
            if (commandText == null || commandText.Length == 0)
            {
                throw new ArgumentNullException("commandText");
            }
            string hashKey = connectionString + ":" + commandText;
            SqlParameter[] cachedParameters = SqlHelperParameterCache.paramCache[hashKey] as SqlParameter[];
            if (cachedParameters == null)
            {
                return null;
            }
            return SqlHelperParameterCache.CloneParameters(cachedParameters);
        }
        /// <summary>
        /// 取得存储过程的参数数组
        /// </summary>
        /// <param name="connectionString">有效的数据库连接串</param>
        /// <param name="spName">存储过程名称</param>
        /// <returns>取到的参数数组</returns>
        public static SqlParameter[] GetSpParameterSet(string connectionString, string spName)
        {
            return SqlHelperParameterCache.GetSpParameterSet(connectionString, spName, false);
        }
        /// <summary>
        /// 取得存储过程的参数数组
        /// </summary>
        /// <param name="connectionString">有效的数据库连接串</param>
        /// <param name="spName">存储过程名称</param>
        /// <param name="includeReturnValueParameter">指示返回的参数值是否要被包含的结果里</param>
        /// <returns>取到的参数数组</returns>
        public static SqlParameter[] GetSpParameterSet(string connectionString, string spName, bool includeReturnValueParameter)
        {
            if (connectionString == null || connectionString.Length == 0)
            {
                throw new ArgumentNullException("connectionString");
            }
            if (spName == null || spName.Length == 0)
            {
                throw new ArgumentNullException("spName");
            }
            SqlParameter[] result;
            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                SqlParameter[] param = SqlHelperParameterCache.GetSpParameterSetInternal(connection, spName, includeReturnValueParameter);
                result = param;
            }
            return result;
        }
        /// <summary>
        /// 取得存储过程的参数数组
        /// </summary>
        /// <param name="connection">有效的数据库连接对象</param>
        /// <param name="spName">存储过程名称</param>
        /// <returns>取到的参数数组</returns>
        internal static SqlParameter[] GetSpParameterSet(SqlConnection connection, string spName)
        {
            return SqlHelperParameterCache.GetSpParameterSet(connection, spName, false);
        }
        /// <summary>
        /// 取得存储过程的参数数组
        /// </summary>
        /// <param name="connection">有效的数据库连接对象</param>
        /// <param name="spName">存储过程名称</param>
        /// <param name="includeReturnValueParameter">指示返回的参数值是否要被包含的结果里</param>
        /// <returns>取到的参数数组</returns>
        internal static SqlParameter[] GetSpParameterSet(SqlConnection connection, string spName, bool includeReturnValueParameter)
        {
            if (connection == null)
            {
                throw new ArgumentNullException("connection");
            }
            SqlParameter[] result;
            using (SqlConnection clonedConnection = (SqlConnection)((ICloneable)connection).Clone())
            {
                SqlParameter[] param = SqlHelperParameterCache.GetSpParameterSetInternal(clonedConnection, spName, includeReturnValueParameter);
                result = param;
            }
            return result;
        }
        /// <summary>
        /// 取得存储过程的参数数组
        /// </summary>
        /// <param name="connection">有效的数据库连接对象</param>
        /// <param name="spName">存储过程名称</param>
        /// <param name="includeReturnValueParameter">指示返回的参数值是否要被包含的结果里</param>
        /// <returns>取到的参数数组</returns>
        private static SqlParameter[] GetSpParameterSetInternal(SqlConnection connection, string spName, bool includeReturnValueParameter)
        {
            if (connection == null)
            {
                throw new ArgumentNullException("connection");
            }
            if (spName == null || spName.Length == 0)
            {
                throw new ArgumentNullException("spName");
            }
            string hashKey = connection.ConnectionString + ":" + spName + (includeReturnValueParameter ? ":include ReturnValue Parameter" : "");
            SqlParameter[] cachedParameters = SqlHelperParameterCache.paramCache[hashKey] as SqlParameter[];
            if (cachedParameters == null)
            {
                SqlParameter[] spParameters = SqlHelperParameterCache.DiscoverSpParameterSet(connection, spName, includeReturnValueParameter);
                SqlHelperParameterCache.paramCache[hashKey] = spParameters;
                cachedParameters = spParameters;
            }
            return SqlHelperParameterCache.CloneParameters(cachedParameters);
        }
    }
}